<?php

namespace Liuhelong\LaravelAdmin\Wechat\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Liuhelong\LaravelAdmin\Wechat\Models\WechatOffiaccountMessageFunction;
use Liuhelong\LaravelAdmin\Wechat\Models\WechatOffiaccountEventFunction;
use Liuhelong\LaravelAdmin\Wechat\Models\WechatOffiaccountKeyword;
use Liuhelong\LaravelAdmin\Wechat\Models\WechatOffiaccountNew;
use Liuhelong\LaravelAdmin\Wechat\Models\WechatOffiaccountEventLog;
use Illuminate\Support\Str;
use EasyWeChat\Kernel\Messages;
use EasyWeChat\Kernel\Messages\Transfer;
use Log;
use EasyWeChat\Factory;
use Liuhelong\LaravelAdmin\Wechat\Jobs\HandleCsMessageSend;

class WechatController extends Controller
{
    //
    protected $messages=['text','video','voice','image','link','file','location'];
    private $events=['event'];
    private $app;
    
	public function serve(Request $request){
	    Log::debug('微信请求.');
        //为了兼容swoole这里没有从app()方法从容器里拿微信服务容器出来，不然会有点bug，因为微信服务容器是单例，每次请求会将之前的请求数据保持在内存里
	    $this->app = Factory::officialAccount(config('wechat.official_account.default'));
        $this->app['request'] = $request;
        $this->app->server->push(function($message){
            return $this->handleMessage($message);
        });

        return $this->app->server->serve();
	}
	
	//消息或事件处理只会处理匹配到的第一个类，如果有比较多的业务逻辑，可以考虑放到触发事件中额外触发其他事件或者直接写一个事件中
	public function handleMessage($message){
	    
	    Log::debug($message);
	    
	    //easywechat 会自动屏蔽消息回复回调中的错误和异常，所以这里加一个catch
        try{
    	    
    	    // 消息处理
    	    if (in_array($message['MsgType'],$this->messages)) {
    	            	        
    	        $message_handles = WechatOffiaccountMessageFunction::where('type',$message['MsgType'])->status()->orderBy('id','desc')->get();
    	        
    	        foreach($message_handles as $message_handle){
    	            if( $this->tryHandle($message_handle,$message['Content']??'')){
    	                return $this->handleReply($message_handle,$message);
    	            }
    	        }
                
                //兜底回复
                $message_handle_default = WechatOffiaccountMessageFunction::where('type','default')->status()->first();
                if( $message_handle_default ){
                    return $this->handleReply($message_handle_default,$message);
                }
            }
            
    	    /***
    	    *** 事件处理
    	    *** 需注意的是，未关注状态下的扫码事件，会触发关注事件，返回扫码事件的key，这时候如果两个事件都有自动回复，跳过关注的自动回复
    	    ***/
            if(in_array($message['MsgType'],$this->events)) {                
                
                // 未关注的扫码事件
                if($message['Event'] == 'subscribe' && Str::of($message['EventKey'])->is('qrscene_*')){
					
					$event_handle_subscribe = WechatOffiaccountEventFunction::where('type','subscribe')->status()->first();
					
                    // 处理关注事件不返回数据
                    if($event_handle_subscribe){
                        $this->handleReply($event_handle_subscribe,$message);
        	            $this->eventLog($message,$event_handle_subscribe);
                    }
					
                    // 手动触发扫码事件
                    $message['Event'] = 'SCAN';
                    $message['EventKey'] = Str::after($message['EventKey'], 'qrscene_');
                }
                
                
    	        $event_handles = WechatOffiaccountEventFunction::where('type',$message['Event'])->status()->orderBy('key_type','desc')->get();
    	        
    	        $event_function = false;
    	        $return_data = null;
    	        foreach($event_handles as $event_handle){
    	            if( $this->tryHandle($event_handle,$message['EventKey']??'')){
    	                $event_function = $event_handle;
    	                $return_data = $this->handleReply($event_handle,$message);
    	            }
    	        }
    	        
    	        $this->eventLog($message,$event_function);
    	        return $return_data;
            }
            
            return null;
        }catch(\Exception $e){
            Log::error($e);
        }
	    
	}
	
	//寻找匹配的处理方法
	public function tryHandle($class_handle,$text){
	    if(!$class_handle->key_type || !$class_handle->key){
            return true;
        }
        
	    switch($class_handle->key_type){
            case('equal'):
                if($class_handle->key==$text){
                    return true;
                }
                return false;
                
            case('like'):
                if(Str::of($text)->is($class_handle->key)){
                    return true;
                }
                return false;
            
            case('reg'):
                if(preg_match($class_handle->key,$text)){
                    return true;
                }
                return false;
            default:
                return true;
        }
	}
	
	public function handleReply($class_handle,$message) {
	    //增加客服消息，因为希望把客服消息放到最后发出，所以用队列任务分发
	    HandleCsMessageSend::dispatchAfterResponse($class_handle,$message);
	    
	    //如果有绑定事件，先处理事件,事件中也可以进行消息回复
	    
	    if($class_handle->function){
	        $functions = explode('@',$class_handle->function);
	        
	        $class = new $functions[0]($message);
	        $function = $functions[1]??'index';
	        
	        $response = $class->$function();
	        if($response){
	            return $response;
	        }
	    }
	    
		// 如果有转发客服，消息转发给客服
	    if($class_handle->is_transfer){
	        return new Transfer($class_handle->customer_service_account);
	    }
		// 消息回复
	    if($class_handle->reply_type && $class_handle->reply_content){
    	    switch($class_handle->reply_type){
    	        case('text'):
    	            return new Messages\Text($class_handle->reply_content);
    	        case('image'):
    	            return new Messages\Image($class_handle->reply_content);
    	        case('voice'):
    	            return new Messages\Voice($class_handle->reply_content);
    	        case('video'):
    	            $video = $this->app->material->get($class_handle->reply_content);
    	            return new Messages\Video($class_handle->reply_content,[$video['title'],$video['description']]);
    	        case('news'):
    	            $new = WechatOffiaccountNew::find($class_handle->reply_content);
    	            $items = [
                        new Messages\NewsItem([
                            'title'       => $new->title,
                            'description' => $new->description,
                            'url'         => $new->url,
                            'image'       => $new->image,
                        ]),
                    ];
    	            return new Messages\News($items);
    	        
    	    }
	    }
	    
	}
	
	//事件记录
	public function eventLog($message,$event_function){
	    WechatOffiaccountEventLog::create([
	        'event_function_id'=>$event_function?$event_function->id:0,
	        'openid'=>$message['FromUserName'], 
	        'event'=>$message['Event'], 
	        'key'=>$message['EventKey']??'', 
        ]);
	}
}
